package lenfs;

import lenfs.impl.ServerImpl;
import lenfs.util.ProgMap;
import lenfs.util.XDR;
import lenfs.util.constants.RPCConst;

import org.jboss.netty.buffer.ChannelBuffer;
import org.jboss.netty.channel.ChannelHandlerContext;
import org.jboss.netty.channel.ChannelStateEvent;
import org.jboss.netty.channel.ExceptionEvent;
import org.jboss.netty.channel.MessageEvent;
import org.jboss.netty.channel.SimpleChannelHandler;

public class ServerHandler extends SimpleChannelHandler {
	private final Server server;

	public ServerHandler(final Server server) {
		this.server = server;
	}

	private void replyDeniedMismatch(final XDR srcPacket, long xid) {
		final XDR reply = new XDR(24, srcPacket.getChannel(),
				srcPacket.getAddress());
		reply.addUInt(xid);
		reply.addUInt(RPCConst.REPLY);
		reply.addUInt(RPCConst.MSG_DENIED);
		reply.addUInt(RPCConst.RPC_MISMATCH);
		reply.addUInt(RPCConst.VERSION);
		reply.addUInt(RPCConst.VERSION);
		reply.send();
	}

	private void processXdrPacket(final XDR packet) {
		long xid = packet.getUInt();
		if (xid == -1) {
			packet.close();
			return;
		}
		long rpcType = packet.getUInt();

		if (rpcType != RPCConst.CALL) {
			packet.close();
			return;
		}

		long rpcVersion = packet.getUInt();
		if (rpcVersion != RPCConst.VERSION) {
			replyDeniedMismatch(packet, xid);
			return;
		}
		long prog = packet.getUInt();
		long version = packet.getUInt();
		long proc = packet.getUInt();

		packet.skipAuth();
		packet.skipAuth();

		final ProgMap progMap = server.getProg(prog);

		if (progMap == null) {
			ServerImpl.voidError(packet, xid, RPCConst.PROG_UNAVAIL);
			return;
		}

		final ServerImpl server = progMap.get(version);

		if (server == null) {
			ServerImpl.versionError(packet, xid, RPCConst.PROG_MISMATCH,
					progMap.getLow(), progMap.getHigh());
			return;
		}

		server.checkPacket(packet, xid, proc);
	}

	public void messageReceived(ChannelHandlerContext ctx, MessageEvent e) {
		ChannelBuffer buf = (ChannelBuffer) e.getMessage();
		processXdrPacket(new XDR(buf, e.getChannel(), e.getRemoteAddress()));
	}

	public void exceptionCaught(ChannelHandlerContext ctx, ExceptionEvent e) {
		e.getCause().printStackTrace();
		e.getChannel().close();
	}

	public void channelConnected(ChannelHandlerContext ctx, ChannelStateEvent e) {
	}
}
